package com.ice.base.util;

import cn.hutool.core.collection.CollectionUtil;
import com.arcsoft.face.FaceFeature;
import com.ice.base.constant.HCNetSDK;
import com.ice.base.model.SysLoginModel;
import com.ice.body.hardware.model.DeviceFace;
import com.ice.body.hardware.service.DeviceFaceService;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;

import static com.arcsoft.face.toolkit.ImageFactory.getRGBData;

/**
 * 人脸识别工具类
 */
@Component
public class FaceUtil {

    private Logger logger = LoggerFactory.getLogger(FaceUtil.class);

    @Autowired
    private DeviceFaceService deviceFaceService;
    @Autowired
    FaceEngineTool faceEngineTool;
    @Autowired
    private RedisUtil redisUtil;

    static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE;
    static int lUserID = -1;//用户句柄
    static int m_lSetCardCfgHandle = -1; //下发卡长连接句柄
    static int m_lSetFaceCfgHandle = -1; //下发人脸长连接句柄
    static int dwState = -1; //下发卡数据状态
    static int dwFaceState = -1; //下发人脸数据状态
    static int iCharEncodeType = 0;//设备字符集


    public void setOneFace(String strCardNo, DeviceFace faceModel) throws InterruptedException {
        HCNetSDK.NET_DVR_DEVICEINFO m_strDeviceInfo= new HCNetSDK.NET_DVR_DEVICEINFO();
        // 用户注册
        int lUserID2 = HCNetSDK.INSTANCE.NET_DVR_Login(faceModel.getIp(),(short)Integer.parseInt(faceModel.getPort()),faceModel.getUserName(),faceModel.getPwd(),m_strDeviceInfo);
        // 确定登录状态
        if(lUserID2 == -1){
            HCNetSDK.INSTANCE.NET_DVR_Logout(lUserID2);
            System.out.println("登录失败 ,错误代码"+HCNetSDK.INSTANCE.NET_DVR_GetLastError());
            return;
        }

        HCNetSDK.NET_DVR_FACE_COND struFaceCond = new HCNetSDK.NET_DVR_FACE_COND();
        struFaceCond.read();
        struFaceCond.dwSize = struFaceCond.size();
        struFaceCond.byCardNo = strCardNo.getBytes();
        struFaceCond.dwFaceNum = 1;  //下发一张
        struFaceCond.dwEnableReaderNo = 1;//人脸读卡器编号
        struFaceCond.write();
        Pointer ptrStruFaceCond = struFaceCond.getPointer();

        m_lSetFaceCfgHandle = hCNetSDK.NET_DVR_StartRemoteConfig(lUserID2, HCNetSDK.NET_DVR_SET_FACE, ptrStruFaceCond, struFaceCond.size(),null ,null);
        if (m_lSetFaceCfgHandle == -1){
            System.out.println("建立下发人脸长连接失败，错误码为"+hCNetSDK.NET_DVR_GetLastError());
            return;
        }else{
            System.out.println("建立下发人脸长连接成功！");
        }

        HCNetSDK.NET_DVR_FACE_RECORD struFaceRecord = new HCNetSDK.NET_DVR_FACE_RECORD();
        struFaceRecord.read();
        struFaceRecord.dwSize = struFaceRecord.size();

        for (int i = 0; i < HCNetSDK.ACS_CARD_NO_LEN; i++){
            struFaceRecord.byCardNo[i] = 0;
        }
        for (int i = 0; i <  strCardNo.length(); i++){
            struFaceRecord.byCardNo[i] = strCardNo.getBytes()[i];
        }

        /*****************************************
         * 从本地文件里面读取JPEG图片二进制数据
         *****************************************/
        FileInputStream picfile = null;
        int picdataLength = 0;
        try{
            picfile = new FileInputStream(new File(BaseUtil.getVideoPath()+"faceImg\\"+strCardNo+".jpg"));
        }catch(FileNotFoundException e){
            e.printStackTrace();
        }

        try{
            picdataLength = picfile.available();
        }catch(IOException e1){
            e1.printStackTrace();
        }
        if(picdataLength < 0){
            System.out.println("input file dataSize < 0");
            return;
        }

        HCNetSDK.BYTE_ARRAY ptrpicByte = new HCNetSDK.BYTE_ARRAY(picdataLength);
        try {
            picfile.read(ptrpicByte.byValue);
            picfile.close();
        } catch (IOException e2) {
            e2.printStackTrace();
        }
        ptrpicByte.write();
        struFaceRecord.dwFaceLen  = picdataLength;
        struFaceRecord.pFaceBuffer  = ptrpicByte.getPointer();
        struFaceRecord.write();


        HCNetSDK.NET_DVR_FACE_STATUS struFaceStatus = new HCNetSDK.NET_DVR_FACE_STATUS();
        struFaceStatus.read();
        struFaceStatus.dwSize = struFaceStatus.size();
        struFaceStatus.write();

        IntByReference pInt = new IntByReference(0);

        while(true){
            dwFaceState = hCNetSDK.NET_DVR_SendWithRecvRemoteConfig(m_lSetFaceCfgHandle, struFaceRecord.getPointer(), struFaceRecord.size(),struFaceStatus.getPointer(), struFaceStatus.size(),  pInt);
            struFaceStatus.read();
            if(dwFaceState == -1){
                System.out.println("NET_DVR_SendWithRecvRemoteConfig接口调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
                break;
            }else if(dwFaceState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEEDWAIT){
                System.out.println("配置等待");
                Thread.sleep(10);
                continue;
            }else if(dwFaceState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED){
                System.out.println("下发人脸失败, 卡号: " + new String(struFaceStatus.byCardNo).trim() + ", 错误码：" + hCNetSDK.NET_DVR_GetLastError());
                break;
            }else if(dwFaceState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION){
                System.out.println("下发人脸异常, 卡号: " + new String(struFaceStatus.byCardNo).trim() + ", 错误码：" + hCNetSDK.NET_DVR_GetLastError());
                break;
            }else if(dwFaceState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS){
                if (struFaceStatus.byRecvStatus != 1){
                    System.out.println("下发人脸失败，人脸读卡器状态" + struFaceStatus.byRecvStatus + ", 卡号：" + new String(struFaceStatus.byCardNo).trim());
                    break;
                }else{
                    System.out.println("下发人脸成功, 卡号: " + new String(struFaceStatus.byCardNo).trim() + ", 状态：" + struFaceStatus.byRecvStatus);
                }
                continue;
            }else if(dwFaceState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
                System.out.println("下发人脸完成");
                break;
            }
        }

        if(!hCNetSDK.NET_DVR_StopRemoteConfig(m_lSetFaceCfgHandle)){
            System.out.println("NET_DVR_StopRemoteConfig接口调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
        }else{
            System.out.println("NET_DVR_StopRemoteConfig接口成功");
        }

        hCNetSDK.NET_DVR_Logout(lUserID2);
//        hCNetSDK.NET_DVR_Cleanup();
    }

    public void GetFaceCfg(SysLoginModel faceUser, String strCardNo, DeviceFace faceModel) throws Exception{
        String ip = faceModel.getIp();
        if (!this.Login(ip, (short) Integer.parseInt(faceModel.getPort()),faceModel.getUserName(),faceModel.getPwd())) {
            logger.error("人脸下发用户ip:" + faceModel.getIp() + "登陆失败！");
            return;
        }
        HCNetSDK.NET_DVR_FACE_COND struFaceCond = new HCNetSDK.NET_DVR_FACE_COND();
        struFaceCond.read();
        struFaceCond.dwSize = struFaceCond.size();
        struFaceCond.dwFaceNum = 1; //查询一个人脸参数
        struFaceCond.dwEnableReaderNo = 1;//读卡器编号

        for (int j = 0; j < HCNetSDK.ACS_CARD_NO_LEN; j++){
            struFaceCond.byCardNo[j] = 0;
        }
        System.arraycopy(strCardNo.getBytes(), 0, struFaceCond.byCardNo, 0, strCardNo.getBytes().length);

        struFaceCond.write();

        int m_lHandle = hCNetSDK.NET_DVR_StartRemoteConfig(lUserID, HCNetSDK.NET_DVR_GET_FACE, struFaceCond.getPointer(), struFaceCond.size(),null ,null);
        if (m_lHandle == -1){
            System.out.println("建立查询人脸参数长连接失败，错误码为"+hCNetSDK.NET_DVR_GetLastError());
            return;
        }else{
            System.out.println("建立查询人脸参数长连接成功！");
        }

        //查询结果
        HCNetSDK.NET_DVR_FACE_RECORD struFaceRecord = new HCNetSDK.NET_DVR_FACE_RECORD();
        struFaceRecord.read();
        String filePath = "";
        while(true){
            dwState = hCNetSDK.NET_DVR_GetNextRemoteConfig(m_lHandle, struFaceRecord.getPointer(), struFaceRecord.size());
            struFaceRecord.read();
            if(dwState == -1){
                System.out.println("NET_DVR_GetNextRemoteConfig查询人脸调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
                break;
            }else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEEDWAIT){
                System.out.println("查询中，请等待...");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                continue;
            }
            else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED){
                System.out.println("获取人脸参数失败, 卡号: " + strCardNo);
                break;
            }else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS){
                if ((struFaceRecord.dwFaceLen > 0) && (struFaceRecord.pFaceBuffer != null)) {
                    FileOutputStream fout;
                    try {

                        filePath = BaseUtil.getVideoPath()+"faceImg\\"+strCardNo+".jpg";
                        fout = new FileOutputStream(filePath);
                        //将字节写入文件
                        long offset = 0;
                        ByteBuffer buffers = struFaceRecord.pFaceBuffer.getByteBuffer(offset, struFaceRecord.dwFaceLen);
                        byte[] bytes = new byte[struFaceRecord.dwFaceLen];
                        buffers.rewind();
                        buffers.get(bytes);
                        fout.write(bytes);
                        fout.close();
                        System.out.println("获取人脸参数成功, 卡号: " + strCardNo + ", 图片保存路径: " + filePath);
                        //下发虹软面部识别 动态存入Redis
                        Integer id  = faceUser.getId();


                        if(!redisUtil.hHasKey("faceMap",id.toString())|| "".equals(redisUtil.hget("faceMap",id.toString()))){
                            FaceFeature faceFeature = faceEngineTool.pathGetFace(filePath);
                            try{
                                redisUtil.hset("faceMap",id.toString(),faceFeature);
                            }catch(Exception e){
                                e.printStackTrace();
                            }
                        }
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                continue;
            }else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
                System.out.println("获取人脸参数完成");
                break;
            }
        }

        if(!hCNetSDK.NET_DVR_StopRemoteConfig(m_lHandle)){
            System.out.println("NET_DVR_StopRemoteConfig接口调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
        }else{
            System.out.println("NET_DVR_StopRemoteConfig接口成功");
        }

        HashMap<String,Object> paramMap = new HashMap<>();
        //楼层标志
        paramMap.put("warehouseFlag",faceUser.getWarehouseFlag());
        List<DeviceFace> list = deviceFaceService.getByFaceFlag(paramMap);
        if(CollectionUtil.isNotEmpty(list)){
            list.forEach(a->{
                if(!a.getIp().equals(ip)){
                    try {
                        this.setOneFace(strCardNo,a);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });

        }
        hCNetSDK.NET_DVR_Logout(lUserID);
    }

    public void addUserForFace(SysLoginModel sysLogin, String ip, String port, String userName, String pwd) {

        String strCardNo = sysLogin.getCardNo();
        try {
            if (!this.Login(ip, (short) Integer.parseInt(port), userName, pwd)) {
                logger.error("人脸下发用户ip:" + ip + "登陆失败！");
                return;
            }
            HCNetSDK.NET_DVR_CARD_COND struCardCond = new HCNetSDK.NET_DVR_CARD_COND();
            struCardCond.read();
            struCardCond.dwSize = struCardCond.size();
            struCardCond.dwCardNum = 1;  //下发一张
            struCardCond.write();
            Pointer ptrStruCond = struCardCond.getPointer();

            m_lSetCardCfgHandle = hCNetSDK.NET_DVR_StartRemoteConfig(lUserID, HCNetSDK.NET_DVR_SET_CARD, ptrStruCond, struCardCond.size(), null, null);
            if (m_lSetCardCfgHandle == -1) {
                System.out.println("建立下发卡长连接失败，错误码为" + hCNetSDK.NET_DVR_GetLastError());
                return;
            } else {
                System.out.println("建立下发卡长连接成功！");
            }

            HCNetSDK.NET_DVR_CARD_RECORD struCardRecord = new HCNetSDK.NET_DVR_CARD_RECORD();
            struCardRecord.read();
            struCardRecord.dwSize = struCardRecord.size();

            for (int i = 0; i < HCNetSDK.ACS_CARD_NO_LEN; i++) {
                struCardRecord.byCardNo[i] = 0;
            }
            for (int i = 0; i < strCardNo.length(); i++) {
                struCardRecord.byCardNo[i] = strCardNo.getBytes()[i];
            }

            struCardRecord.byCardType = 1; //普通卡
            struCardRecord.byLeaderCard = 0; //是否为首卡，0-否，1-是
            struCardRecord.byUserType = 0;
            struCardRecord.byDoorRight[0] = 1; //门1有权限
            struCardRecord.wCardRightPlan[0] = (short) 1;//关联门计划模板，使用了前面配置的计划模板

            struCardRecord.struValid.byEnable = 1;    //卡有效期使能，下面是卡有效期从2021-1-1 11:11:11到2030-1-1 11:11:11
            struCardRecord.struValid.struBeginTime.wYear = 2021;
            struCardRecord.struValid.struBeginTime.byMonth = 1;
            struCardRecord.struValid.struBeginTime.byDay = 1;
            struCardRecord.struValid.struBeginTime.byHour = 11;
            struCardRecord.struValid.struBeginTime.byMinute = 11;
            struCardRecord.struValid.struBeginTime.bySecond = 11;
            struCardRecord.struValid.struEndTime.wYear = 2031;
            struCardRecord.struValid.struEndTime.byMonth = 1;
            struCardRecord.struValid.struEndTime.byDay = 1;
            struCardRecord.struValid.struEndTime.byHour = 11;
            struCardRecord.struValid.struEndTime.byMinute = 11;
            struCardRecord.struValid.struEndTime.bySecond = 11;

            struCardRecord.dwEmployeeNo = Integer.parseInt(sysLogin.getUserNo()); //工号
            struCardRecord.byCardPassword = sysLogin.getPassWord().getBytes();// 密码未加密时存入人脸设备
            String realName = sysLogin.getRealName();
            byte[] strCardName = new byte[0];
            if ((iCharEncodeType == 0) || (iCharEncodeType == 1) || (iCharEncodeType == 2)) {
                try {
                    strCardName = realName.getBytes("GBK");  //姓名
                } catch (Exception e) {
                    e.printStackTrace();
                }
                for (int i = 0; i < HCNetSDK.NAME_LEN; i++) {
                    struCardRecord.byName[i] = 0;
                }
                System.arraycopy(strCardName, 0, struCardRecord.byName, 0, strCardName.length);
            }

            if (iCharEncodeType == 6) {
                try {
                    strCardName = realName.getBytes("UTF-8");  //姓名
                } catch (Exception e) {
                    e.printStackTrace();
                }
                for (int i = 0; i < HCNetSDK.NAME_LEN; i++) {
                    struCardRecord.byName[i] = 0;
                }
                System.arraycopy(strCardName, 0, struCardRecord.byName, 0, strCardName.length);
            }

            struCardRecord.write();

            HCNetSDK.NET_DVR_CARD_STATUS struCardStatus = new HCNetSDK.NET_DVR_CARD_STATUS();
            struCardStatus.read();
            struCardStatus.dwSize = struCardStatus.size();
            struCardStatus.write();

            IntByReference pInt = new IntByReference(0);

            while (true) {
                dwState = hCNetSDK.NET_DVR_SendWithRecvRemoteConfig(m_lSetCardCfgHandle, struCardRecord.getPointer(), struCardRecord.size(), struCardStatus.getPointer(), struCardStatus.size(), pInt);
                struCardStatus.read();
                if (dwState == -1) {
                    System.out.println("NET_DVR_SendWithRecvRemoteConfig接口调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
                    break;
                } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEEDWAIT) {
                    System.out.println("配置等待");
                    Thread.sleep(10);
                    continue;
                } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED) {
                    System.out.println("下发卡失败, 卡号: " + new String(struCardStatus.byCardNo).trim() + ", 错误码：" + struCardStatus.dwErrorCode);
                    break;
                } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION) {
                    System.out.println("下发卡异常, 卡号: " + new String(struCardStatus.byCardNo).trim() + ", 错误码：" + struCardStatus.dwErrorCode);
                    break;
                } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS) {
                    if (struCardStatus.dwErrorCode != 0) {
                        System.out.println("下发卡成功,但是错误码" + struCardStatus.dwErrorCode + ", 卡号：" + new String(struCardStatus.byCardNo).trim());
                    } else {
                        System.out.println("下发卡成功, 卡号: " + new String(struCardStatus.byCardNo).trim() + ", 状态：" + struCardStatus.byStatus);
                    }
                    continue;
                } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
                    System.out.println("下发卡完成");
                    break;
                }

            }

            if (!hCNetSDK.NET_DVR_StopRemoteConfig(m_lSetCardCfgHandle)) {
                System.out.println("NET_DVR_StopRemoteConfig接口调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
            } else {
                System.out.println("NET_DVR_StopRemoteConfig接口成功");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public boolean Login(String ip,short port,String userName,String pwd){
        boolean returnFlag = false;
        //注册
        HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();//设备登录信息

        String m_sDeviceIP = ip;//设备ip地址
        m_strLoginInfo.sDeviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
        System.arraycopy(m_sDeviceIP.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, m_sDeviceIP.length());

        String m_sUsername = userName;//设备用户名
        m_strLoginInfo.sUserName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
        System.arraycopy(m_sUsername.getBytes(), 0, m_strLoginInfo.sUserName, 0, m_sUsername.length());

        String m_sPassword = pwd;//设备密码
        m_strLoginInfo.sPassword = new byte[HCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];
        System.arraycopy(m_sPassword.getBytes(), 0, m_strLoginInfo.sPassword, 0, m_sPassword.length());

        m_strLoginInfo.wPort = port;
        m_strLoginInfo.bUseAsynLogin = false; //是否异步登录：0- 否，1- 是
        m_strLoginInfo.write();

        HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();//设备信息
        lUserID = hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);
        if (lUserID == -1){
            System.out.println("登录失败，错误码为"+hCNetSDK.NET_DVR_GetLastError());
        } else{
            System.out.println("登录成功！");
            iCharEncodeType = m_strDeviceInfo.byCharEncodeType;
            returnFlag = true;
        }
        return returnFlag;
    }
}